﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using UtilZ.Dotnet.Ex.Model;

namespace UtilZ.Dotnet.WindowsDesktopEx.WPF
{
    /// <summary>
    /// ComboBox多选项数据源
    /// </summary>
    /// <typeparam name="T">数据项类型</typeparam>
    public class ComboBoxMultiSelectemItemsSource<T> : NotifyPropertyChangedAbs, IList<T>
        where T : class, ICheckItem
    {
        /// <summary>
        /// 当没有Check项时显示的文本
        /// </summary>
        private readonly string _emptyText;

        /// <summary>
        /// 数据源
        /// </summary>
        private readonly ObservableCollection<T> _observableCollection = new ObservableCollection<T>();

        /// <summary>
        /// 获取绑定数据源
        /// </summary>
        public IEnumerable Source
        {
            get { return _observableCollection; }
        }


        private readonly T _allCheckItem;
        /// <summary>
        /// 获取全选项
        /// </summary>
        public T AllCheckItem
        {
            get { return _allCheckItem; }
        }

        private string _selectedItemsText;
        /// <summary>
        /// 获取或设置选中项显示文本
        /// </summary>
        public string SelectedItemsText
        {
            get { return _selectedItemsText; }
            set
            {
                _selectedItemsText = value;
                this.OnRaisePropertyChanged(nameof(SelectedItemsText));
            }
        }


        /// <summary>
        /// 已注册CheckedChanged项列表
        /// </summary>
        private readonly List<T> _registCheckedChangedList = new List<T>();


        #region 选中改变通知
        /// <summary>
        /// 是否选中改变事件
        /// </summary>
        public event EventHandler<CheckedArgs<T>> CheckedChanged;

        /// <summary>
        /// 集合选中项改变通知回调
        /// </summary>
        private readonly Action<CheckedArgs> _checkedChangedAction;

        /// <summary>
        /// 集合选中项改变通知回调
        /// </summary>
        private readonly Action<CheckedArgs<T>> _checkedChangedActionT;

        private void OnRaiseCheckedChangedAction(T source, bool oldChecked, bool newChecked)
        {
            var handler = this.CheckedChanged;
            if (this._checkedChangedActionT != null || handler != null)
            {
                var args = new CheckedArgs<T>(source, oldChecked, newChecked);
                if (handler != null)
                {
                    handler(this, args);
                }

                if (this._checkedChangedActionT != null)
                {
                    this._checkedChangedActionT(args);
                }
            }

            if (this._checkedChangedAction != null)
            {
                var args = new CheckedArgs(source, oldChecked, newChecked);
                this._checkedChangedAction.Invoke(args);
            }
        }
        #endregion




        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="emptyText">当没有Check项时显示的文本</param>
        /// <param name="allCheckItem">全选项</param>
        /// <param name="list">初始集合</param>
        private ComboBoxMultiSelectemItemsSource(string emptyText, T allCheckItem = null, IList<T> list = null)
            : base()
        {
            this._emptyText = emptyText;
            this._allCheckItem = allCheckItem;

            if (list != null && list.Count > 0)
            {
                this._observableCollection = new ObservableCollection<T>(list);
                if (allCheckItem != null && !list.Contains(allCheckItem))
                {
                    this.Insert(0, allCheckItem);
                }

                int count = 0;
                int checkedItemCount = 0;
                foreach (var item in list)
                {
                    if (allCheckItem != item)
                    {
                        count++;
                        if (item.IsChecked)
                        {
                            checkedItemCount++;
                        }
                    }

                    this.RegistCheckedChanged(item);
                }

                if (allCheckItem != null)
                {
                    bool allChecked = false;
                    if (count == 0 && checkedItemCount == 0
                        || checkedItemCount == count)
                    {
                        allChecked = true;
                    }

                    allCheckItem.SetIsChecked(allChecked, false);
                }
            }
            else
            {
                this._observableCollection = new ObservableCollection<T>();
                if (allCheckItem != null)
                {
                    this.Add(allCheckItem);
                }
            }

            this.UpdateSelectedItemsText();
        }

        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="emptyText">当没有Check项时显示的文本</param>
        /// <param name="checkedChangedAction">集合选中项改变通知回调</param>
        /// <param name="allCheckItem">全选项</param>
        /// <param name="list">初始集合</param>
        public ComboBoxMultiSelectemItemsSource(string emptyText, Action<CheckedArgs> checkedChangedAction = null, T allCheckItem = null, IList<T> list = null)
            : this(emptyText, allCheckItem, list)
        {
            this._checkedChangedAction = checkedChangedAction;
        }

        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="emptyText">当没有Check项时显示的文本</param>
        /// <param name="allCheckItem">全选项</param>
        /// <param name="checkedChangedAction">集合选中项改变通知回调</param>
        /// <param name="list">初始集合</param>
        public ComboBoxMultiSelectemItemsSource(string emptyText, T allCheckItem = null, Action<CheckedArgs<T>> checkedChangedAction = null, IList<T> list = null)
            : this(emptyText, allCheckItem, list)
        {
            this._checkedChangedActionT = checkedChangedAction;
        }


        private bool _manualCommitCollectionChanged = false;
        /// <summary>
        /// 手动提交集合改变操作
        /// </summary>
        public void ManualCommitCollectionChanged()
        {
            this._manualCommitCollectionChanged = true;
        }

        /// <summary>
        /// 自动提交集合改变操作
        /// </summary>
        public void AutoCommitCollectionChanged()
        {
            this._manualCommitCollectionChanged = false;
            this.CommitCollectionChanged();
        }

        /// <summary>
        /// 提交集合改变操作
        /// </summary>
        public void CommitCollectionChanged()
        {
            foreach (T item in this._observableCollection)
            {
                this.RegistCheckedChanged(item);
            }

            this.UpdateChecked();
            this.UpdateSelectedItemsText();
        }



        private void RegistCheckedChanged(T item)
        {
            item.CheckedChanged += Item_CheckedChanged;
            if (!this._registCheckedChangedList.Contains(item))
            {
                this._registCheckedChangedList.Add(item);
            }
        }

        private void UnRegistCheckedChanged(T item)
        {
            item.CheckedChanged -= Item_CheckedChanged;
            this._registCheckedChangedList.Remove(item);
        }

        private void Item_CheckedChanged(object sender, CheckedArgs<ICheckItem> e)
        {
            T checkItem = (T)e.Source;
            this.UpdateChecked(checkItem);
            this.UpdateSelectedItemsText();
            this.OnRaiseCheckedChangedAction(checkItem, e.OldChecked, e.NewChecked);
        }



        private void UpdateSelectedItemsText()
        {
            if (this._manualCommitCollectionChanged)
            {
                return;
            }

            string selectedItemsText;
            if (this._allCheckItem != null && this._allCheckItem.IsChecked)
            {
                selectedItemsText = "全部";
            }
            else
            {
                StringBuilder sb = null;
                foreach (T item in this._observableCollection)
                {
                    if (item.IsChecked)
                    {
                        if (sb == null)
                        {
                            sb = new StringBuilder();
                        }

                        if (sb.Length > 0)
                        {
                            sb.Append("|");
                        }

                        sb.Append(item.Text);
                    }
                }

                if (sb != null)
                {
                    selectedItemsText = sb.ToString();
                }
                else
                {
                    selectedItemsText = this._emptyText;
                }
            }

            this.SelectedItemsText = selectedItemsText;
        }

        private void UpdateChecked()
        {
            if (this._manualCommitCollectionChanged)
            {
                return;
            }

            if (this._allCheckItem == null)
            {
                return;
            }

            int checkedCount = 0;
            foreach (T item in this._observableCollection)
            {
                if (object.ReferenceEquals(this._allCheckItem, item))
                {
                    continue;
                }

                if (item.IsChecked)
                {
                    checkedCount++;
                }
            }

            if (checkedCount == this.Count - 1)
            {
                this._allCheckItem.SetIsChecked(true, false);
            }
            else
            {
                this._allCheckItem.SetIsChecked(false, false);
            }
        }

        private void UpdateChecked(T selectedItem)
        {
            if (this._manualCommitCollectionChanged)
            {
                return;
            }

            if (this._allCheckItem == null)
            {
                return;
            }

            if (object.ReferenceEquals(selectedItem, this._allCheckItem))
            {
                foreach (var item in this._observableCollection)
                {
                    item.SetIsChecked(selectedItem.IsChecked, false);
                }
            }
            else
            {
                int checkedCount = 0;
                foreach (T item in this._observableCollection)
                {
                    if (object.ReferenceEquals(this._allCheckItem, item))
                    {
                        continue;
                    }

                    if (item.IsChecked)
                    {
                        checkedCount++;
                    }
                }

                if (checkedCount == this.Count - 1)
                {
                    this._allCheckItem.SetIsChecked(true, true);
                }
                else
                {
                    this._allCheckItem.SetIsChecked(false, false);
                }
            }
        }










        #region IList
        /// <summary>
        /// 
        /// </summary>
        public int Count
        {
            get { return this._observableCollection.Count; }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool IsReadOnly
        {
            get { return false; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public T this[int index]
        {
            get
            {
                return this._observableCollection[index];
            }
            set
            {
                this.UnRegistCheckedChanged(this._observableCollection[index]);
                this._observableCollection[index] = value;
                this.RegistCheckedChanged(value);
                this.UpdateChecked();
                this.UpdateSelectedItemsText();
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public int IndexOf(T item)
        {
            return this._observableCollection.IndexOf(item);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <param name="item"></param>
        public void Insert(int index, T item)
        {
            this._observableCollection.Insert(index, item);
            this.RegistCheckedChanged(item);
            this.UpdateChecked(item);
            this.UpdateSelectedItemsText();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        public void RemoveAt(int index)
        {
            this.UnRegistCheckedChanged(this._observableCollection[index]);
            this._observableCollection.RemoveAt(index);
            this.UpdateChecked();
            this.UpdateSelectedItemsText();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        public void Add(T item)
        {
            this._observableCollection.Add(item);
            this.RegistCheckedChanged(item);
            this.UpdateChecked(item);
            this.UpdateSelectedItemsText();
        }

        /// <summary>
        /// 
        /// </summary>
        public void Clear()
        {
            foreach (T item in this._observableCollection)
            {
                this.UnRegistCheckedChanged(item);
            }

            this._observableCollection.Clear();
            this.UpdateSelectedItemsText();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Contains(T item)
        {
            return this._observableCollection.Contains(item);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="array"></param>
        /// <param name="arrayIndex"></param>
        public void CopyTo(T[] array, int arrayIndex)
        {
            this._observableCollection.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool Remove(T item)
        {
            if (this._observableCollection.Remove(item))
            {
                this.UnRegistCheckedChanged(item);
                this.UpdateChecked();
                this.UpdateSelectedItemsText();
                return true;
            }

            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IEnumerator<T> GetEnumerator()
        {
            return this._observableCollection.GetEnumerator();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
        #endregion
    }
}
